// Package config
// @File: config.go
// @Description: 解析加载配置文件，初始化全局参数，初始化日志
// @Author: YaoRuiQi
// @Date: 2025/8/23 02:49

package config

import (
	"GinStandardTemplate/internal/constant"
	"GinStandardTemplate/utils/logger"
	"GinStandardTemplate/utils/pathUtils"
	"GinStandardTemplate/utils/stringUtils"
	"log"
	"os"
	"path/filepath"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/yaml.v3"
	gormLog "gorm.io/gorm/logger"
)

// config
// @Description: 配置文件结构体
type config struct {
	App struct {
		Name      string `yaml:"name"`
		Mode      string `yaml:"mode"`
		SecretKey []byte // jwt密钥
	} `yaml:"app"`
	Server struct {
		Host    string `yaml:"host"`
		Port    int    `yaml:"port"`
		Timeout struct {
			Read  int `yaml:"read"`
			Write int `yaml:"write"`
			Idle  int `yaml:"idle"`
		} `yaml:"timeout"`
		MaxHeader int `yaml:"maxHeader"`
	} `yaml:"server"`
	Database struct {
		// 基础参数
		Dialect  string `yaml:"dialect"`
		Host     string `yaml:"host"`
		Port     int    `yaml:"port"`
		Username string `yaml:"username"`
		Password string `yaml:"password"`
		DBName   string `yaml:"dbname"`
		// 连接池参数
		MaxIdleConns    int `yaml:"maxIdleConns"`
		MaxOpenConns    int `yaml:"maxOpenConns"`
		ConnMaxLifetime int `yaml:"connMaxLifetime"`
		InitTimeout     int `yaml:"initTimeout"`
		// 特定数据库参数
		Charset    string // MySQL 专用，例如 utf8mb4
		SSLMode    string // Postgres 专用：disable / require
		TimeZone   string // Postgres 专用，建议 Asia/Shanghai
		SQLiteFile string // SQLite 文件路径
		// Gorm 日志级别
		GormLogLevel gormLog.LogLevel
	} `yaml:"database"`
	Log struct {
		Level        string `yaml:"level"`
		ShowTerminal bool   `yaml:"showTerminal"`
		Maxsize      int    `yaml:"maxsize"`
		MaxAge       int    `yaml:"maxAge"`
		MaxBackups   int    `yaml:"maxBackups"`
	} `yaml:"log"`
	Redis struct {
		Host               string `yaml:"host"`
		Port               int    `yaml:"port"`
		Password           string `yaml:"password"`
		DB                 int    `yaml:"database"`
		MaxRetries         int    `yaml:"maxRetries"`
		MinRetryBackoff    int    `yaml:"minRetryBackoff"`
		MaxRetryBackoff    int    `yaml:"maxRetryBackoff"`
		PoolSize           int    `yaml:"poolSize"`
		MinIdleConns       int    `yaml:"minIdleConns"`
		ConnMaxIdleTime    int    `yaml:"connMaxIdleTime"`
		ReadTimeout        int    `yaml:"readTimeout"`
		WriteTimeout       int    `yaml:"writeTimeout"`
		DialTimeout        int    `yaml:"dialTimeout"`
		PoolTimeout        int    `yaml:"poolTimeout"`
		HealthCheckRetries int    `yaml:"healthCheckretries"`
		Prefix             string `yaml:"prefix"`
	} `yaml:"redis"`
	Path struct {
		BaseDir        string // 项目根目录
		LogDir         string // 日志目录
		StaticDir      string // 静态文件目录
		I18nLocalesDir string // 国际化语言文件目录
	}
}

// GlobalConfig 全局配置
var GlobalConfig config

// InitGlobalConfigAndLog
//
//	@Description: 初始化配置文件与日志
func InitGlobalConfigAndLog() {
	exePath, err := os.Executable()
	if err != nil {
		log.Fatalf("获取可执行文件路径失败: %v", err)
	}
	baseDir := filepath.Dir(exePath)

	// 加载全局配置文件
	//  优先读取当前目录下的 config.yaml 文件
	//  如果当前目录下没有 config.yaml 文件，则从环境变量中读取
	var configYamlPath string
	tempPath := filepath.Join(baseDir, "config.yaml")
	if isExists, err := pathUtils.PathExists(tempPath); err != nil {
		logger.Logger.Fatal("无法检测配置文件是否存在", zap.Error(err), zap.String("path", tempPath))
	} else if isExists {
		configYamlPath = tempPath
	} else if envYamlPath := os.Getenv("CONFIG_YAML_PATH"); envYamlPath != "" {
		configYamlPath = envYamlPath
	} else {
		log.Fatalf("未获取到yaml配置文件路径")
	}

	if yamlData, err := os.ReadFile(configYamlPath); err != nil {
		log.Fatalf("读取全局配置文件失败: %v", err)
	} else if err = yaml.Unmarshal(yamlData, &GlobalConfig); err != nil {
		log.Fatalf("解析全局配置文件失败: %v", err)
	}

	// 设置文件夹路径，并初始化文件夹
	GlobalConfig.Path.BaseDir = baseDir
	GlobalConfig.Path.LogDir = filepath.Join(baseDir, "logs")
	GlobalConfig.Path.StaticDir = filepath.Join(baseDir, "static")
	GlobalConfig.Path.I18nLocalesDir = filepath.Join(GlobalConfig.Path.StaticDir, "i18n_locales")
	for _, dirPath := range [3]string{
		GlobalConfig.Path.LogDir,
		GlobalConfig.Path.StaticDir,
		GlobalConfig.Path.I18nLocalesDir,
	} {
		if _, err := os.Stat(dirPath); os.IsNotExist(err) {
			if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
				logger.Logger.Fatal("创建目录失败", zap.String("dirPath", dirPath), zap.Error(err))
			}
		}
	}

	// 初始化日志
	var zapLevel zapcore.Level
	switch GlobalConfig.Log.Level {
	case constant.LoggerLevelDebug:
		zapLevel = zap.DebugLevel
	case constant.LoggerLevelInfo:
		zapLevel = zap.InfoLevel
	case constant.LoggerLevelWarn:
		zapLevel = zap.WarnLevel
	case constant.LoggerLevelError:
		zapLevel = zap.ErrorLevel
	default:
		logger.Logger.Fatal(
			"全局配置文件日志级别参数错误，可选参数: [debug, info, warn, error]",
			zap.String("currentLevel", GlobalConfig.Log.Level),
		)
	}
	logger.InitLogger(
		GlobalConfig.Path.LogDir,
		GlobalConfig.Log.ShowTerminal,
		zapLevel,
		GlobalConfig.Log.Maxsize,
		GlobalConfig.Log.MaxAge,
		GlobalConfig.Log.MaxBackups,
	)

	switch GlobalConfig.App.Mode {
	case constant.AppModeRelease:
		GlobalConfig.Database.GormLogLevel = gormLog.Warn
	case constant.AppModeDebug:
		GlobalConfig.Database.GormLogLevel = gormLog.Info
	case constant.AppModeTest:
		GlobalConfig.Database.GormLogLevel = gormLog.Info
	default:
		logger.Logger.Fatal("无效的启动模式", zap.String("mode", GlobalConfig.App.Mode))
	}

	// 初始化jwt密钥，密钥长度为32位，并保存在 StaticDir / jwt_secret.txt 文件中
	SecretKeyFilePath := filepath.Join(GlobalConfig.Path.StaticDir, "jwt_secret.txt")
	if _, err := os.Stat(SecretKeyFilePath); os.IsNotExist(err) {
		if secretKey, err := stringUtils.GenerateRandomString(stringUtils.RandomCharOptions{
			UseDigits: true,
			UseLower:  true,
			UseUpper:  true,
			UseSymbol: true,
		}, 32); err != nil {
			logger.Logger.Fatal("生成jwt密钥失败", zap.Error(err))
		} else {
			GlobalConfig.App.SecretKey = []byte(secretKey)
		}
		if err := os.WriteFile(SecretKeyFilePath, GlobalConfig.App.SecretKey, 0644); err != nil {
			logger.Logger.Fatal("保存jwt密钥失败", zap.Error(err))
		}
	} else {
		if GlobalConfig.App.SecretKey, err = os.ReadFile(SecretKeyFilePath); err != nil {
			logger.Logger.Fatal("读取jwt密钥失败", zap.Error(err))
		}
	}

	// 读取并初始化数据库配置
	if GlobalConfig.Database.Charset == "" {
		GlobalConfig.Database.Charset = "utf8mb4"
	}
	if GlobalConfig.Database.SSLMode == "" {
		GlobalConfig.Database.SSLMode = "disable"
	}
	if GlobalConfig.Database.TimeZone == "" {
		GlobalConfig.Database.TimeZone = "Asia/Shanghai"
	}
	if GlobalConfig.Database.SQLiteFile == "" {
		GlobalConfig.Database.SQLiteFile = filepath.Join(baseDir, "app.database")
	}
	if GlobalConfig.Database.MaxIdleConns <= 0 {
		GlobalConfig.Database.MaxIdleConns = 10
	}
	if GlobalConfig.Database.MaxOpenConns <= 0 {
		GlobalConfig.Database.MaxOpenConns = 20
	}
	if GlobalConfig.Database.ConnMaxLifetime <= 0 {
		GlobalConfig.Database.ConnMaxLifetime = 60
	}
	if GlobalConfig.Database.InitTimeout <= 0 {
		GlobalConfig.Database.InitTimeout = 5
	}

	// redis配置
	if GlobalConfig.Redis.Prefix == "" {
		GlobalConfig.Redis.Prefix = GlobalConfig.App.Name
	}
}
